use sea_orm::sea_query::SimpleExpr;
use serde::{Deserialize, Deserializer};

use crate::JsonValue;

static RANDSTR_MATCH: once_cell::sync::Lazy<regex::Regex> =
    once_cell::sync::Lazy::new(|| regex::Regex::new(r"^RandStr(?:\((\d+)\))?$").unwrap());

#[derive(Debug, Clone)]
pub enum DefaultValueFunc {
    DateTime,
    Date,
    Timestamp,
    RandStr(usize),
    UuidV4,
    Ulid,
    True,
    False,
}

impl<'de> Deserialize<'de> for DefaultValueFunc {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        String::deserialize(deserializer)?
            .try_into()
            .map_err(serde::de::Error::custom)
    }
}

macro_rules! ss {
    ($re:ident,$value:expr) => {
        if $value == stringify!($re) {
            return Ok(Self::$re);
        }
    };
}

impl TryFrom<String> for DefaultValueFunc {
    type Error = String;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        ss!(DateTime, value);
        ss!(Date, value);
        ss!(Timestamp, value);
        ss!(UuidV4, value);
        ss!(Ulid, value);
        ss!(True, value);
        ss!(False, value);
        if let Some(cap) = RANDSTR_MATCH.captures(&value) {
            return Ok(Self::RandStr(
                cap.get(1)
                    .map(|f| f.as_str().parse::<usize>())
                    .map(|f| f.unwrap_or(12))
                    .unwrap_or(8),
            ));
        }
        Err(format!("unknown variant `{}`", value))
    }
}

impl Into<SimpleExpr> for &DefaultValueFunc {
    fn into(self) -> SimpleExpr {
        match self {
            DefaultValueFunc::DateTime => crate::util::current_datetime().into(),
            DefaultValueFunc::Date => crate::util::current_date().into(),
            DefaultValueFunc::Timestamp => crate::util::current_timestamp().into(),
            DefaultValueFunc::RandStr(size) => crate::util::rand_str(*size).into(),
            DefaultValueFunc::UuidV4 => crate::util::uuid_v4_str().into(),
            DefaultValueFunc::Ulid => crate::util::ulid_str().into(),
            DefaultValueFunc::True => true.into(),
            DefaultValueFunc::False => false.into(),
        }
    }
}

impl Into<JsonValue> for &DefaultValueFunc {
    fn into(self) -> JsonValue {
        match self {
            DefaultValueFunc::DateTime => crate::util::current_datetime().to_string().into(),
            DefaultValueFunc::Date => crate::util::current_date().to_string().into(),
            DefaultValueFunc::Timestamp => crate::util::current_timestamp().timestamp().into(),
            DefaultValueFunc::RandStr(size) => crate::util::rand_str(*size).into(),
            DefaultValueFunc::UuidV4 => crate::util::uuid_v4_str().into(),
            DefaultValueFunc::Ulid => crate::util::ulid_str().into(),
            DefaultValueFunc::True => JsonValue::Bool(true),
            DefaultValueFunc::False => JsonValue::Bool(false),
        }
    }
}
